﻿using Microsoft.Extensions.Logging;
using Net8.Project.Common.Extensions;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;

namespace Net8.Project.Extension
{
    /// <summary>
    /// Redis 缓存操作实现类
    /// </summary>
    [Description("普通缓存建议直接使用ICaching，如需使用Redis队列等功能请使用此类")]
    public class RedisBasket : IRedisBasket
    {
        private readonly ILogger<RedisBasket> _logger;
        private readonly ConnectionMultiplexer _redis;

        public RedisBasket(ILogger<RedisBasket> logger, ConnectionMultiplexer redis)
        {
            _logger = logger;
            _redis = redis;
        }

        /// <summary>
        /// 获取指定数据库
        /// </summary>
        private IDatabase GetDatabase(int db = -1) => _redis.GetDatabase(db);

        /// <summary>
        /// 获取 Redis 服务器
        /// </summary>
        private IServer GetServer()
        {
            var endpoint = _redis.GetEndPoints();
            return _redis.GetServer(endpoint.First());
        }

        #region 基础缓存操作

        /// <inheritdoc/>
        public async Task<string> GetValue(string key)
        {
            return await GetDatabase().StringGetAsync(key);
        }

        /// <inheritdoc/>
        public async Task<TEntity> Get<TEntity>(string key)
        {
            var value = await GetDatabase().StringGetAsync(key);
            return value.HasValue ? value.ToString().ToObject<TEntity>() : default;
        }

        /// <inheritdoc/>
        public async Task Set(string key, object value, TimeSpan cacheTime)
        {
            if (value == null) return;
            var db = GetDatabase();
            if (value is string cacheValue)
            {
                await db.StringSetAsync(key, cacheValue, cacheTime);
            }
            else
            {
                await db.StringSetAsync(key, value.ToJsonByte(), cacheTime);
            }
        }

        /// <inheritdoc/>
        public async Task<bool> Exist(string key)
        {
            return await GetDatabase().KeyExistsAsync(key);
        }

        /// <inheritdoc/>
        public async Task Remove(string key)
        {
            await GetDatabase().KeyDeleteAsync(key);
        }

        /// <inheritdoc/>
        public async Task Clear()
        {
            foreach (var endPoint in _redis.GetEndPoints())
            {
                var server = _redis.GetServer(endPoint);
                foreach (var key in server.Keys())
                {
                    await GetDatabase().KeyDeleteAsync(key);
                }
            }
        }

        #endregion

        #region List 操作

        /// <inheritdoc/>
        public async Task<RedisValue[]> ListRangeAsync(string redisKey)
        {
            return await GetDatabase().ListRangeAsync(redisKey);
        }

        /// <inheritdoc/>
        public async Task<long> ListLeftPushAsync(string redisKey, string redisValue, int db = -1)
        {
            return await GetDatabase(db).ListLeftPushAsync(redisKey, redisValue);
        }

        /// <inheritdoc/>
        public async Task<long> ListRightPushAsync(string redisKey, string redisValue, int db = -1)
        {
            return await GetDatabase(db).ListRightPushAsync(redisKey, redisValue);
        }

        /// <inheritdoc/>
        public async Task<long> ListRightPushAsync(string redisKey, IEnumerable<string> redisValue, int db = -1)
        {
            var redisList = redisValue?.Select(x => (RedisValue)x).ToArray() ?? Array.Empty<RedisValue>();
            return await GetDatabase(db).ListRightPushAsync(redisKey, redisList);
        }

        /// <inheritdoc/>
        public async Task<T> ListLeftPopAsync<T>(string redisKey, int db = -1) where T : class
        {
            var redisValue = await GetDatabase(db).ListLeftPopAsync(redisKey);
            return string.IsNullOrEmpty(redisValue) ? null : redisValue.ToString().ToObject<T>();
        }

        /// <inheritdoc/>
        public async Task<T> ListRightPopAsync<T>(string redisKey, int db = -1) where T : class
        {
            var redisValue = await GetDatabase(db).ListRightPopAsync(redisKey);
            return string.IsNullOrEmpty(redisValue) ? null : redisValue.ToString().ToObject<T>();
        }

        /// <inheritdoc/>
        public async Task<string> ListLeftPopAsync(string redisKey, int db = -1)
        {
            var value = await GetDatabase(db).ListLeftPopAsync(redisKey);
            return value.HasValue ? value.ToString() : null;
        }

        /// <inheritdoc/>
        public async Task<string> ListRightPopAsync(string redisKey, int db = -1)
        {
            var value = await GetDatabase(db).ListRightPopAsync(redisKey);
            return value.HasValue ? value.ToString() : null;
        }

        /// <inheritdoc/>
        public async Task<long> ListLengthAsync(string redisKey, int db = -1)
        {
            return await GetDatabase(db).ListLengthAsync(redisKey);
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int db = -1)
        {
            var result = await GetDatabase(db).ListRangeAsync(redisKey);
            return result.Select(o => o.ToString());
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int start, int stop, int db = -1)
        {
            var result = await GetDatabase(db).ListRangeAsync(redisKey, start, stop);
            return result.Select(o => o.ToString());
        }

        /// <inheritdoc/>
        public async Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0, int db = -1)
        {
            return await GetDatabase(db).ListRemoveAsync(redisKey, redisValue, type);
        }

        /// <inheritdoc/>
        public async Task ListClearAsync(string redisKey, int db = -1)
        {
            await GetDatabase(db).ListTrimAsync(redisKey, 1, 0);
        }

        #endregion
    }
}